#include "transcoder_core.h"


/*x264 library lock*/
pthread_mutex_t x264_t_lock = PTHREAD_MUTEX_INITIALIZER;

char* SRC_FILENAME = NULL;
int OUTPUT_VIDEO_NUM = 0;   
int DEC_THREAD_CNT  = 0;
int THREAD_COUNT = 0;
int CONSOL_OUTPUT = 1;
int ID_OF_THE_FIRST_CORE_TO_BIND = 0;
int ENC_THREAD_CNT = 0;
float AVERAGE_FPS = 0;
int FPS_COUNT = 0;
int gop_size = 100;
int FULL_GOP_BUFFER_SIZE = 10;

float total_frame_cnt;
float total_fps_cnt; 

EncodeStruct ess[7] =
{
	{0, 1920, 1080, 8000000, AV_PIX_FMT_YUV420P, "", 1, "", "", 0, 0},
	{1, 1920, 1080, 4000000, AV_PIX_FMT_YUV420P, "", 1,"","", 0, 0},
	{2, 1280, 720, 2300000, AV_PIX_FMT_YUV420P, "", 1,"", "", 0, 0},
	{3, 1280, 720, 1400000, AV_PIX_FMT_YUV420P, "", 1, "", "", 0, 0},
	{4, 1280, 720, 1300000, AV_PIX_FMT_YUV420P, "", 1, "", "", 0, 0},
	{5, 640, 360, 800000, AV_PIX_FMT_YUV420P, "", 1,"",  "", 0, 0},
	{6, 640, 360, 600000, AV_PIX_FMT_YUV420P, "", 1, "", "", 0, 0}
};


void allocate_threads() {
	static double encode_threads[7] = {4, 2, 1.5, 1, 1, 0.5, 0.5};
	int encoding_tasks[7];

	if(THREAD_COUNT < 2) {
		fprintf(stdout, "Error 21: Error: thread number less than 2.\n");
		exit(1);
	}

	double encode_threads_per_decode_thread = 0;
	int i;
	for(i = 0; i < OUTPUT_VIDEO_NUM; i++) {
		if(ess[i].dst_width >= 1920 && ess[i].dst_height >= 1080
				&& ess[i].bitrate >= 8000000) {
			encode_threads_per_decode_thread += encode_threads[0];
			encoding_tasks[i] = 0;
		} else if(ess[i].dst_width >= 1920 && ess[i].dst_height >= 1080
				&& ess[i].bitrate >= 4000000) {
			encode_threads_per_decode_thread += encode_threads[1];
			encoding_tasks[i] = 1;
		} else if(ess[i].dst_width >= 1280 && ess[i].dst_height >= 720
				&& ess[i].bitrate >= 2300000) {
			encode_threads_per_decode_thread += encode_threads[2];
			encoding_tasks[i] = 2;
		} else if(ess[i].dst_width >= 1280 && ess[i].dst_height >= 720
				&& ess[i].bitrate >= 1400000) {
			encode_threads_per_decode_thread += encode_threads[3];
			encoding_tasks[i] = 3;
		} else if(ess[i].dst_width >= 1280 && ess[i].dst_height >= 720
				&& ess[i].bitrate >= 1300000) {
			encode_threads_per_decode_thread += encode_threads[4];
			encoding_tasks[i] = 4;
		} else if(ess[i].dst_width >= 640 && ess[i].dst_height >= 360
				&& ess[i].bitrate >= 800000) {
			encode_threads_per_decode_thread += encode_threads[5];
			encoding_tasks[i] = 5;
		} else if(ess[i].dst_width >= 640 && ess[i].dst_height >= 360
				&& ess[i].bitrate >= 600000) {
			encode_threads_per_decode_thread += encode_threads[6];
			encoding_tasks[i] = 6;
		} else {
			encode_threads_per_decode_thread += encode_threads[6];
			encoding_tasks[i] = 6;
		}
	}

	int decode_threads = THREAD_COUNT / (encode_threads_per_decode_thread + 1);

	int allocated_encode_threads = 0;

	for (i = 0; i < OUTPUT_VIDEO_NUM; i++) {
		ess[i].thread_num = (int)((double)decode_threads * encode_threads[encoding_tasks[i]] + 0.5);
		allocated_encode_threads += ess[i].thread_num;
	}

	int threads_remain = THREAD_COUNT - allocated_encode_threads - decode_threads;
	if(threads_remain > 0) {
		/*decode_threads++;*/
		/*threads_remain--;*/
		for(i = 0; i < OUTPUT_VIDEO_NUM; i++) {
			ess[i].thread_num += threads_remain / OUTPUT_VIDEO_NUM;
		}
		ess[0].thread_num += threads_remain % OUTPUT_VIDEO_NUM;
	}
	DEC_THREAD_CNT = decode_threads;

	for (i = 0; i < OUTPUT_VIDEO_NUM; i++) {
		printf("Encoding task %d has %d threads.\n", encoding_tasks[i], ess[i].thread_num);
	}
	printf("Decoding task has %d threads.\n", DEC_THREAD_CNT);

	return;
}


struct timeval start, end;
int cnt;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

int main (int argc, char **argv)
{
	int ret = 0;
	int i = 0;
	int j = 0;
	char command[256];
	int devNull = open("/dev/null", O_WRONLY);
	dup2(devNull, STDERR_FILENO);

	OUTPUT_VIDEO_NUM = 0;

	printf("\n");
	for(i = 1; i < argc; ++i){
		char* arg = argv[i];
		if(strcmp("-i", arg) == 0){
			SRC_FILENAME = argv[++i];
			printf("Input File Name: %s\n", argv[i]);
		}else if(strcmp("-c:v", arg) == 0){
			if(strcmp("libx264", argv[++i]) == 0){
				printf("Encode Lib is %s\n", argv[i]);
				sprintf(ess[OUTPUT_VIDEO_NUM].output_format, "h264");
			}
			if(strcmp("libx265", argv[i]) == 0){
				printf("Encode Lib is %s\n", argv[i]);
				sprintf(ess[OUTPUT_VIDEO_NUM].output_format, "h265");
			}
		}else if(strcmp("-b:v", arg) == 0){
			sscanf(argv[++i], "%dk", &ess[OUTPUT_VIDEO_NUM].bitrate);
			ess[OUTPUT_VIDEO_NUM].bitrate *= 1000;
			printf("Bitrate :%s\n", argv[i]);
		}
		else if(strcmp("-s", arg) == 0){
			sscanf(argv[++i], "%dx%d", &ess[OUTPUT_VIDEO_NUM].dst_width, &ess[OUTPUT_VIDEO_NUM].dst_height);
			printf("Resolution :%s\n", argv[i]);
		}
		else if(strcmp("-c:a", arg) == 0){
			printf("%s\n", argv[++i]);

			ess[OUTPUT_VIDEO_NUM].output_filename = argv[++i];
			struct timeval t;
			gettimeofday(&t, NULL);
			long timestamp = t.tv_sec * 1000000 + t.tv_usec;
			sprintf(ess[OUTPUT_VIDEO_NUM].output_dir, "%s_%ld", ess[OUTPUT_VIDEO_NUM].output_filename, timestamp);
			printf("Output File name: %s\n", argv[i]);
			printf("Output Dir name: %s\n", ess[OUTPUT_VIDEO_NUM].output_dir);
			++OUTPUT_VIDEO_NUM;
		}
		else if(strcmp("-threads", arg) == 0){
			THREAD_COUNT = atoi(argv[++i]);
			printf("Thread Count: %s\n", argv[i]);
		}
		else if(strcmp("-fc", arg) == 0){
			ID_OF_THE_FIRST_CORE_TO_BIND = atoi(argv[++i]);
			printf("First Bingding Core: %s\n", argv[i]);
		}
		/*else if(strcmp("-etn", arg) == 0){*/
			/*ess[OUTPUT_VIDEO_NUM].thread_num = atoi(argv[++i]);*/
			/*printf("Encode Thread Count: %s\n", argv[i]);*/
		/*}*/
		else if(strcmp("-quiet", arg) == 0){
			++i;
			CONSOL_OUTPUT = 0;
		} else if (strcmp("-gs", arg) == 0) {
			gop_size = atoi(argv[++i]); //atoi is not secure
		} else if (strcmp("-fs", arg) == 0) {
			FULL_GOP_BUFFER_SIZE = atoi(argv[++i]);
		}
	}

	printf("gs %d fs %d", gop_size, FULL_GOP_BUFFER_SIZE);

	if(CONSOL_OUTPUT == 0){
		dup2(devNull, STDOUT_FILENO);
	}

	for(i = 0; i < OUTPUT_VIDEO_NUM; i++) {
		sprintf(command, "rm -rf %s && mkdir %s", ess[i].output_dir, ess[i].output_dir);
		system(command);
	}

	if(OUTPUT_VIDEO_NUM < 1 || OUTPUT_VIDEO_NUM > 6) {
		fprintf(stdout, "Error 22: output video number should be in range [1, 6].\n");
		exit(1);
	}

	allocate_threads();

	for (i = 0; i < OUTPUT_VIDEO_NUM; i++) {
		ENC_THREAD_CNT += ess[i].thread_num;
	}

	initialize();

	pthread_t* decode_threads = xmalloc(DEC_THREAD_CNT * sizeof(pthread_t));

	if(DEC_THREAD_CNT > 1){
		for (i = 0; i < DEC_THREAD_CNT; i++) {
			pthread_create(&decode_threads[i], NULL, (void * (*)(void *))decode_period, NULL);
		}

		for (i = 0; i < DEC_THREAD_CNT; i++) {
			pthread_detach(decode_threads[i]);
		}
	}else{
			pthread_create(&decode_threads[0], NULL, (void * (*)(void *))decode, NULL);
			pthread_detach(decode_threads[0]);
	}

	pthread_t* encode_threads = xmalloc(ENC_THREAD_CNT * sizeof(pthread_t));
	int thread_id = 0;
    //for one way output
    for (i = 0; i < OUTPUT_VIDEO_NUM; i++) {
        for (j = 0; j < ess[i].thread_num; j++) {
            pthread_create(&encode_threads[thread_id++], NULL,
                    (void * (*)(void *))encode_and_scale_thread, (void *)&ess[i]);
        }
    }

   /* EncodeStructList es_list;*/
	/*es_list.es[0] = &ess[0];*/
	/*es_list.es[1] = &ess[1];*/
	/*es_list.es[2] = &ess[2];*/

	//es_list.es_num = 3;
	/*es_list.es_num = 2;*/
	/*es_list.es_num = 1;*/

    /*
	 *for (int i = 0; i < ENC_THREAD_CNT; i++)
	 *{
	 *    pthread_create(&encode_threads[thread_id++], NULL,
	 *             (void * (*)(void *))multi_encode_thread, (void *)&es_list);
	 *}
     */
	for (i = 0; i < thread_id; i++) {
		pthread_join(encode_threads[i], NULL);
	}

 	gettimeofday(&end, NULL);

    printf("ni da ye\n");
 	// printf("output %f/n", ( 1000000 *(end.tv_sec - start.tv_sec) + (end.tv_usec - start.tv_usec))/1000.0);
	printf("Stable FPS is: %.2f \n", total_frame_cnt / total_fps_cnt);

	return ret < 0;
}
